#region using statements

using System;

using AeonOfStrife.Combat;

using AeonOfStrifeDatatypes.Combat;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using NGen;
using NGen.Managers.GameObject;
using NGen.Systems.Alarms;
using NGen.Systems.Graphics;

#endregion

namespace AeonOfStrife.Objects.Units.UnitStates
{
	public class UnitStateAttack : UnitState, IAlarmListener<UnitAlarmType> {

		#region local fields
		
		protected readonly AnimatedSprite[] m_attackAnimations;

		protected readonly AttackInfo[] m_attackInfos;

		protected readonly AnimatedSprite m_idleAnimation;

		private AlarmObject<UnitAlarmType> m_attackAlarm;

		private bool m_attackReady = true;

		#endregion

		#region constructors

		public UnitStateAttack(AttackInfo[] p_animationInfo, AnimationInfo p_idleInfo)
			: base(p_idleInfo) {
			m_idleAnimation = AnimatedSprite;
			m_attackInfos = p_animationInfo;
			m_attackAnimations = new AnimatedSprite[p_animationInfo.Length];
			for(var i = 0; i < m_attackInfos.Length; i++) {
				var spriteSheet = GameEngine.GameContent.Load<Texture2D>(m_attackInfos[i].m_animationInfo.m_spritesheet);
				m_attackAnimations[i] = new AnimatedSprite(
					spriteSheet,
					m_attackInfos[i].m_animationInfo.m_frameSize,
					m_attackInfos[i].m_animationInfo.m_spritesheetDimensions,
					UnitConstants.c_FRAME_DELAY);
			}

			m_attackAlarm = new AlarmObject<UnitAlarmType>(this, 0, UnitAlarmType.ATTACK_ALARM) {
				Enabled = true
			};
		}

		#endregion

		#region UnitState implementation

		public override void Update(GameTime p_gameTime) {
			base.Update(p_gameTime);
			if(!Parent.InRangeOfTarget()) {
				Parent.Change(Parent.m_runState);
			}
			if(m_attackReady) {
				Attack();
			}
		}

		public override void OnEnter() {
		}

		public override void OnExit() {
			AnimatedSprite = m_idleAnimation;
		}

		#endregion

		#region local methods

		internal void Attack() {
			if(m_attackAnimations.Length == 0) {
				throw new Exception("No attacks found, were they loaded in the xml?");
			}

			//select a random attack from the list of attacks
			var random = (float) GameEngine.Random.NextDouble();
			var attackNum = 0;
			float totals = 0;
			for(var i = 0; i < m_attackAnimations.Length; i++) {
				totals += m_attackInfos[i].m_attackChance;
				if(random < totals) {
					attackNum = i;
					break;
				}
			}

			//change animation to randomed attack animation
			AnimatedSprite = m_attackAnimations[attackNum];
			AnimatedSprite.AnimationEndedEvent += AnimationEndedEvent;
			AnimatedSprite.ResetAnimation();

			switch(m_attackInfos[attackNum].m_attackType) {
				case AttackType.MAGIC:
				case AttackType.RANGED:
					CreateProjectile(m_attackInfos[attackNum]);
					break;
				case AttackType.MELEE:
					Parent.AttackTarget(m_attackInfos[attackNum].DamageValues);
					break;
			}

			//reset the attack alarm
			m_attackAlarm.TimeRemaning = m_attackInfos[attackNum].AttackDelay;
			Parent.m_alarmSystem.Add(m_attackAlarm);
			m_attackReady = false;
		}

		private void AnimationEndedEvent(string p_animationName) {
			AnimatedSprite = m_idleAnimation;
			m_idleAnimation.ResetAnimation();
		}

		public void OnAlarmEvent(UnitAlarmType p_message) {
			m_attackReady = true;
		}

		private void CreateProjectile(AttackInfo p_attackInfo) {
			var projectile = new Projectile(
				p_attackInfo.DamageValues,
				p_attackInfo.m_projectileInfo,
				p_attackInfo.m_attackType, 
				Parent,
				Parent.Target);
			projectile.m_zDepth = Parent.m_zDepth;
			GameEngine.GetService<IManagerGameObjects>().AddObject(projectile);
		}

		#endregion
	}
}